package com.gameone.test.tree;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class TreeNode<E> implements Cloneable {
	private E attr;
	private TreeNode<?> parent;
	private List<TreeNode<?>> childs = new LinkedList<TreeNode<?>>();

	public TreeNode() {
		super();
	}

	public TreeNode(E attr) {
		super();
		this.attr = attr;
	}

	// private List<TreeNode<?>> siblings;
	// private TreeNode<?> previousSibling;
	// private TreeNode<?> nextSibling;

	public boolean hasChild() {
		return !childs.isEmpty();
	}

	public void appendChild(TreeNode<?> node) {
		node.parent = this;
		childs.add(node);
	}

	public void insertBefore(int index, TreeNode<?> node) {
		node.parent = this;
		childs.add(index, node);
	}

	public void insertBefore(TreeNode<?> beforeNode, TreeNode<?> node) {
		int index = childs.indexOf(beforeNode);
		insertBefore(index, node);
	}

	public void insertAfter(int index, TreeNode<?> node) {
		node.parent = this;
		childs.add(index + 1, node);
	}

	public void insertAfter(TreeNode<?> afterNode, TreeNode<?> node) {
		int index = childs.indexOf(afterNode);
		insertAfter(index, node);
	}

	public TreeNode<?> removeChild(int index) {
		TreeNode<?> node = childs.get(index);
		node.parent = null;

		return childs.remove(index);
	}

	public void removeChild(TreeNode<?> node) {
		int index = childs.indexOf(node);
		if (index < 0) {
			throw new IllegalArgumentException("The [node] not found in the childs");
		} else {
			node.parent = null;

			childs.remove(index);
		}
	}

	public TreeNode<?> replaceChild(int index, TreeNode<?> node) {
		TreeNode<?> beReplacedNode = childs.get(index);
		beReplacedNode.parent = null;

		node.parent = this;
		return childs.set(index, node);
	}

	public void replaceChild(TreeNode<?> beReplacedNode, TreeNode<?> node) {
		int index = childs.indexOf(beReplacedNode);
		if (index < 0) {
			throw new IllegalArgumentException("The [beReplacedNode] not found in the childs");
		} else {
			beReplacedNode.parent = null;

			node.parent = this;
			childs.set(index, node);
		}
	}

	/**
	 * @return the attr
	 */
	public E getAttr() {
		return attr;
	}

	/**
	 * @param attr
	 *            the attr to set
	 */
	public void setAttr(E attr) {
		this.attr = attr;
	}

	/**
	 * @return the parent
	 */
	public TreeNode<?> getParent() {
		return parent;
	}

	/**
	 * @return the childs
	 */
	public List<TreeNode<?>> getChilds() {
		return childs;
	}

	/**
	 * @return the siblings
	 */
	public List<TreeNode<?>> getSiblings() {
		if (parent == null || parent.childs.size() == 1) {
			return null;
		}

		int index = parent.childs.indexOf(this);

		List<TreeNode<?>> siblings = new ArrayList<TreeNode<?>>(parent.childs.size() - 1);
		if (index > 0) {
			siblings.addAll(parent.childs.subList(0, index));
		}
		if (index < parent.childs.size() - 1) {
			siblings.addAll(parent.childs.subList(index + 1, parent.childs.size()));
		}

		return siblings;
	}

	/**
	 * @return the previousSibling
	 */
	public TreeNode<?> getPreviousSibling() {
		if (parent == null || parent.childs.size() == 1) {
			return null;
		}

		int index = parent.childs.indexOf(this);
		if (index == 0) {
			return null;
		} else {
			return parent.childs.get(index - 1);
		}
	}

	/**
	 * @return the nextSibling
	 */
	public TreeNode<?> getNextSibling() {
		if (parent == null || parent.childs.size() == 1) {
			return null;
		}

		int index = parent.childs.indexOf(this);
		if (index == parent.childs.size() - 1) {
			return null;
		} else {
			return parent.childs.get(index + 1);
		}
	}
}
